import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.Level;
import java.util.logging.Logger;

import povrayexample.Main;
import povrayexample.ThreadWait;
import xwchclientapi.XWCHClient;
import xwchclientapi.util.FileRef;

public class XWCHHandlerPovray {

	private ExecutorService m_pool;

	public XWCHHandlerPovray() {
		m_pool = Executors.newFixedThreadPool(20);
	}

	/**
	 * 
	 * @param IdClient
	 *            XWCH client identifier
	 * @param addressserver
	 *            XWCH coordinator address
	 * @param filename
	 *            the name of the pov file
	 * @param height
	 *            the height of the image to generate
	 * @param width
	 *            the width of the image to generate
	 * @param k
	 *            the number of slices
	 * @param povrayModId
	 *            module identifier of povray binary
	 * @param combineModId
	 *            module identifier of combine binary
	 * @param regExpPovRayHosts
	 *            regular expression that matches XWCH workers, where to run
	 *            PovRay jobs
	 * @param regExpCombineHost
	 *            regular expression that matches XWCH workers, where to run
	 *            PovRay jobs
	 */
	public void XWCHRunPovRay(String IdClient, String addressserver,
			String image, int height, int width, int k, String povrayModId,
			String combineModId, String regExpPovRayHosts,
			String regExpCombineHost, String applicationName) {

		try {

			// ======================= Prepare the XWCH environment
			// ================================
			XWCHClient c = new XWCHClient(addressserver, ".", IdClient);// instantiate
																		// the
																		// XWCHClient
																		// object
			Logger.getLogger(Main.class.getName()).log(Level.INFO,
					"initialize the application Context");
			if (!c.init()) { // contact the XWCH coordinator and warehouses
				System.out.println("ERROR INIT");
				return;
			}

			String appid = c.addApplication(applicationName); // Insert
																			// the
																			// application
																			// into
																			// the
																			// XWCH
																			// system
			if (appid == null) {
				return;
			}
			System.out.println("ApplicationId : " + appid);

			String inputzipfile = image + ".zip";
			String filename = image + ".pov";

			// String inputPath = "exposure.zip"; // input file for the binary
			// PovRay

			// ======================= Prepare PovRay jobs
			// =======================
			int slice = height / k; // we compute the number of jobs to send.
	
			NumberFormat formatter = new DecimalFormat("#0000"); // defined
																	// decimal
																	// format
																	// (to be
																	// used for
																	// the
																	// slices'
																	// names)

			List<String> PovRayJobs = new ArrayList<String>(); // a list to
																// store the
																// jobs
																// identifiers

			FileRef freftask1 = c.addData(inputzipfile, appid); // send input
																// data on
																// warehouses

			if (freftask1 == null) {
				System.out
						.println("Error in sending input data for first task ");
				return;
			}
			final CountDownLatch latch = new CountDownLatch(k); // Synchronization
																// step. It's
																// like a
																// semaphore
																// with a
																// counter "k".
			// Each created job will decrease this counter after completion.
			String job = null;
			String foutid = null;
			String prefixFileName = "chess2"; // prefix name of ppm files
			// ======================= Send the jobs
			// ============================================

			String hosts = (regExpPovRayHosts.isEmpty()) ? "" : ("host;regexp:"
					+ regExpPovRayHosts + ";");// select xwch workers
			for (int i = 0; i < k; i++) {
				int start = (i == 0) ? 0 : (i * slice + 1); // begin of the
															// current slice
				int end = (i + 1) * slice; // end of the current slice
				if (i == (k - 1)) {
					end = height;// last part of image shoule take what remains
									// from image.
				}

				String foutliste = prefixFileName + "_" + formatter.format(i)
						+ ".ppm"; // the name of the image slice to be generated
									// after.
				String filesToCompress = "regexp:" + prefixFileName + "_"
						+ formatter.format(i) + ".*";
				String cmdline = new StringBuilder("megapov.bat +I")
						.append(filename)
						.// prepare the command line corresponding to the
							// current slice
						append(" +FP +H").append(height).append(" +W")
						.append(width).append(" -D +SR").append(start)
						.append(" +ER").append(end).append(" +O")
						.append(foutliste).toString();

				foutid = c.getUniqueID(); // the name out the output file for
											// the current job.
				job = c.addJob("PovRay task_" + formatter.format(i), // name of
																		// the
																		// job
						appid, // application name
						povrayModId, // module identifier
						cmdline, // command line to execute on the worker
						freftask1.toJobReference(), // input files
						filesToCompress, // files to compress in the file
											// "foutid"
						foutid, // name of the output file (this is a compressed
								// file)
						hosts + "replicate;2"); // replicate the output file on
												// 2 warehouses

				// System.out.println(job + ": " + cmdline); //Display the
				// command line of each submitted job
				PovRayJobs.add(job); // list to register the submitted jobs. We
										// will use it to get the output name of
										// each job.
										// these output files will be the input
										// files for the Combine job (see
										// below).
				if (job != null) {
					Runnable r = new ThreadWait(job, c, latch); // We create a
																// thread that
																// waits for the
																// execution of
																// the job j
					m_pool.execute(r); // We insert the created thread on the pool
				} else {
					Logger.getLogger(Main.class.getName()).log(Level.SEVERE,
							"Job submission fails");
				}
			}// end for

			// ======================= Wait for the jobs
			// ============================================

			System.out.println("\nWaiting for Jobs to COMPLETE ...");
			try {
				latch.await(); // this is a blocking call. We wait until all
								// submitted jobs finish
				m_pool.shutdown(); // we shutdown the pool of threads
			} catch (Exception E) {
				// handle
				m_pool.shutdown(); // in case of exception, shutdown the pool of
									// threads
			}

			// ======================= Prepare for final job
			// =========================================
			// All submitted jobs are finished.
			// Now we will prepare the Combine job. It is a job that will
			// combine all the images slices together into one image.

			String completePPMFile = prefixFileName + "Complete.ppm";

			// command line:combine all files starting with prefix "chess2" into
			// the completeScene.ppm
			String cmdlinegather = "combineppm " + prefixFileName + " "
					+ completePPMFile;

			// input files should be separated by ";"
			// here we retrieve all the outputfiles names of the submitted jobs
			StringBuilder GatherInputFiles = new StringBuilder();
			for (int j = 0; j < PovRayJobs.size(); j++) {
				String jobid = PovRayJobs.get(j);
				String out = c.getJobFileOutName(jobid);
				GatherInputFiles.append(out).append(
						(out.endsWith(";")) ? "" : ";");
			}
			String foutidCombine = c.getUniqueID() + ".zip"; // This is the
																// output name
																// for the job
																// "Gather"
			String combineHost = (regExpCombineHost.isEmpty()) ? ""
					: ("host;regexp:" + regExpCombineHost + ";");// select xwch
																	// workers

			// ======================= send the final job
			// =============================================
			String CombineJobId = c.addJob( // Add the Combine job
					"PovRay Combine picture", // application name
					appid, // application identifier
					combineModId, // binary identifier
					cmdlinegather, // command line to execute on worker
					GatherInputFiles.toString(), // input files
					"regexp:" + prefixFileName + ".*", // regular expression :
														// retrieve only files
														// where the name starts
														// with $prefixFileName
					foutidCombine, // output file name (this is a zipped file)
					combineHost + "replicate;2"); // replicate the output file 2
													// times

			System.out.println("Combine job submitted");

			// ======================= Wait and download the final image
			// ===============================
			c.getJobResult(CombineJobId, foutidCombine);// This is a blocking
														// method call. It Waits
														// until the job ends
														// and retrieves its
														// output file
			System.out.println("Output file (image) donwloaded :"
					+ foutidCombine);
			// ======================= Clean the XWCH system
			// ==========================================
			
			c.endApplication(appid); // removes all the jobs and the application
										// from the XWCH system.
		} catch (Exception e) {
			System.out.println(" Oups !! an error occurs .........");
		}
	}

}
